/*-------------------------------------------------------------------------
 *
 * hstore_subs.c
 *	  hstore 的下标支持函数。
 *
 * 这比 array_subs.c 简单得多，因为下标 hstore 的结果仅仅是一个文本字符串（键的值）。
 * 我们不需要支持数组切片语法，也不需要多个下标。
 * 更不明显的是，由于下标结果从不作为 SQL 容器类型返回，
 * 所以将不会有嵌套赋值的场景，因此我们也不需要 fetch_old 函数。
 * 这意味着我们可以删除 check_subscripts 函数，仅让 fetch 和 assign 函数完成所有工作。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  contrib/hstore/hstore_subs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "executor/execExpr.h"
#include "hstore.h"
#include "nodes/nodeFuncs.h"
#include "nodes/subscripting.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "utils/builtins.h"


/*
 * 完成对hstore的SubscriptingRef表达式的解析分析。
 *
 * 验证只有一个下标，将其强制转换为文本，
 * 并设置SubscriptingRef节点的结果类型。
 */
static void fc_hstore_subscript_transform(SubscriptingRef *fc_sbsref,
						   List *fc_indirection,
						   ParseState *fc_pstate,
						   bool fc_isSlice,
						   bool fc_isAssignment)
{
	A_Indices  *fc_ai;
	Node	   *fc_subexpr;

	/* 我们仅支持单下标、非切片情况 */
	if (fc_isSlice || list_length(fc_indirection) != 1)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("hstore allows only one subscript"),
				 parser_errposition(fc_pstate,
									exprLocation((Node *) fc_indirection))));

	/* 将下标表达式转换为文本类型 */
	fc_ai = linitial_node(A_Indices, fc_indirection);
	Assert(fc_ai->uidx != NULL && fc_ai->lidx == NULL && !fc_ai->is_slice);

	fc_subexpr = transformExpr(fc_pstate, fc_ai->uidx, fc_pstate->p_expr_kind);
	/* 如果还不是文本，尝试强制转换 */
	fc_subexpr = coerce_to_target_type(fc_pstate,
									fc_subexpr, exprType(fc_subexpr),
									TEXTOID, -1,
									COERCION_ASSIGNMENT,
									COERCE_IMPLICIT_CAST,
									-1);
	if (fc_subexpr == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("hstore subscript must have type text"),
				 parser_errposition(fc_pstate, exprLocation(fc_ai->uidx))));

	/* ...并将转换后的下标存储到SubscriptRef节点中 */
	fc_sbsref->refupperindexpr = list_make1(fc_subexpr);
	fc_sbsref->reflowerindexpr = NIL;

	/* 确定下标操作的结果类型；始终为文本 */
	fc_sbsref->refrestype = TEXTOID;
	fc_sbsref->reftypmod = -1;
}

/*
 * 评估hstore的SubscriptingRef获取。
 *
 * 源容器在步骤的结果变量中（它被认为不是NULL，因为
 * 我们将fetch_strict设置为true），下标表达式在
 * upperindex[]数组中。
 */
static void fc_hstore_subscript_fetch(ExprState *fc_state,
					   ExprEvalStep *fc_op,
					   ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	HStore	   *fc_hs;
	text	   *fc_key;
	HEntry	   *fc_entries;
	int			fc_idx;
	text	   *fc_out;

	/* 如果源hstore为null，则不应该到这里 */
	Assert(!(*fc_op->resnull));

	/* 检查下标是否为null */
	if (fc_sbsrefstate->upperindexnull[0])
	{
		*fc_op->resnull = true;
		return;
	}

	/* 好的，获取/解托管hstore和下标 */
	fc_hs = DatumGetHStoreP(*fc_op->resvalue);
	fc_key = DatumGetTextPP(fc_sbsrefstate->upperindex[0]);

	/* 剩下的基本与hstore_fetchval()相同 */
	fc_entries = ARRPTR(fc_hs);
	fc_idx = hstoreFindKey(fc_hs, NULL,
						VARDATA_ANY(fc_key), VARSIZE_ANY_EXHDR(fc_key));

	if (fc_idx < 0 || HSTORE_VALISNULL(fc_entries, fc_idx))
	{
		*fc_op->resnull = true;
		return;
	}

	fc_out = cstring_to_text_with_len(HSTORE_VAL(fc_entries, STRPTR(fc_hs), fc_idx),
								   HSTORE_VALLEN(fc_entries, fc_idx));

	*fc_op->resvalue = PointerGetDatum(fc_out);
}

/*
 * 评估hstore的SubscriptingRef赋值。
 *
 * 输入容器（可能为null）在结果区域，替换值在
 * SubscriptingRefState的replacevalue/replacenull中。
 */
static void fc_hstore_subscript_assign(ExprState *fc_state,
						ExprEvalStep *fc_op,
						ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	text	   *fc_key;
	Pairs		fc_p;
	HStore	   *fc_out;

	/* 检查下标是否为null */
	if (fc_sbsrefstate->upperindexnull[0])
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("hstore subscript in assignment must not be null")));

	/* 好的，获取/解托管下标 */
	fc_key = DatumGetTextPP(fc_sbsrefstate->upperindex[0]);

	/* 为下标 + 替换值创建一个Pairs条目 */
	fc_p.needfree = false;
	fc_p.key = VARDATA_ANY(fc_key);
	fc_p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(fc_key));

	if (fc_sbsrefstate->replacenull)
	{
		fc_p.vallen = 0;
		fc_p.isnull = true;
	}
	else
	{
		text	   *fc_val = DatumGetTextPP(fc_sbsrefstate->replacevalue);

		fc_p.val = VARDATA_ANY(fc_val);
		fc_p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(fc_val));
		fc_p.isnull = false;
	}

	if (*fc_op->resnull)
	{
		/* 只构建一个元素的hstore（参见hstore_from_text） */
		fc_out = hstorePairs(&fc_p, 1, fc_p.keylen + fc_p.vallen);
	}
	else
	{
		/*
		 * 否则，将新键合并到hstore中。基于
		 * hstore_concat。
		 */
		HStore	   *fc_hs = DatumGetHStoreP(*fc_op->resvalue);
		int			fc_s1count = HS_COUNT(fc_hs);
		int			fc_outcount = 0;
		int			fc_vsize;
		char	   *fc_ps1,
				   *fc_bufd,
				   *fc_pd;
		HEntry	   *fc_es1,
				   *fc_ed;
		int			fc_s1idx;
		int			fc_s2idx;

		/* 在不考虑重复的情况下分配结果 */
		fc_vsize = CALCDATASIZE(fc_s1count + 1, VARSIZE(fc_hs) + fc_p.keylen + fc_p.vallen);
		fc_out = palloc(fc_vsize);
		SET_VARSIZE(fc_out, fc_vsize);
		HS_SETCOUNT(fc_out, fc_s1count + 1);

		fc_ps1 = STRPTR(fc_hs);
		fc_bufd = fc_pd = STRPTR(fc_out);
		fc_es1 = ARRPTR(fc_hs);
		fc_ed = ARRPTR(fc_out);

		for (fc_s1idx = fc_s2idx = 0; fc_s1idx < fc_s1count || fc_s2idx < 1; ++fc_outcount)
		{
			int			fc_difference;

			if (fc_s1idx >= fc_s1count)
				fc_difference = 1;
			else if (fc_s2idx >= 1)
				fc_difference = -1;
			else
			{
				int			fc_s1keylen = HSTORE_KEYLEN(fc_es1, fc_s1idx);
				int			fc_s2keylen = fc_p.keylen;

				if (fc_s1keylen == fc_s2keylen)
					fc_difference = memcmp(HSTORE_KEY(fc_es1, fc_ps1, fc_s1idx),
										fc_p.key,
										fc_s1keylen);
				else
					fc_difference = (fc_s1keylen > fc_s2keylen) ? 1 : -1;
			}

			if (fc_difference >= 0)
			{
				HS_ADDITEM(fc_ed, fc_bufd, fc_pd, fc_p);
				++fc_s2idx;
				if (fc_difference == 0)
					++fc_s1idx;
			}
			else
			{
				HS_COPYITEM(fc_ed, fc_bufd, fc_pd,
							HSTORE_KEY(fc_es1, fc_ps1, fc_s1idx),
							HSTORE_KEYLEN(fc_es1, fc_s1idx),
							HSTORE_VALLEN(fc_es1, fc_s1idx),
							HSTORE_VALISNULL(fc_es1, fc_s1idx));
				++fc_s1idx;
			}
		}

		HS_FINALIZE(fc_out, fc_outcount, fc_bufd, fc_pd);
	}

	*fc_op->resvalue = PointerGetDatum(fc_out);
	*fc_op->resnull = false;
}

/*
 * 为hstore子下标操作设置执行状态。
 */
static void fc_hstore_exec_setup(const SubscriptingRef *fc_sbsref,
				  SubscriptingRefState *fc_sbsrefstate,
				  SubscriptExecSteps *fc_methods)
{
	/* 断言我们处理的是一个下标 */
	Assert(fc_sbsrefstate->numlower == 0);
	Assert(fc_sbsrefstate->numupper == 1);
	/* 我们无法在此检查upperprovided[0]，但这必须是真的 */

	/* 返回适当步骤执行函数的指针 */
	fc_methods->sbs_check_subscripts = NULL;
	fc_methods->sbs_fetch = fc_hstore_subscript_fetch;
	fc_methods->sbs_assign = fc_hstore_subscript_assign;
	fc_methods->sbs_fetch_old = NULL;
}

/*
 * hstore_subscript_handler
 *		hstore的下标处理器。
 */
PG_FUNCTION_INFO_V1(hstore_subscript_handler);
Datum hstore_subscript_handler(PG_FUNCTION_ARGS)
{
	static const SubscriptRoutines fc_sbsroutines = {
		.transform = fc_hstore_subscript_transform,
		.exec_setup = fc_hstore_exec_setup,
		.fetch_strict = true,	/* 对于NULL输入，fetch返回NULL */
		.fetch_leakproof = true,	/* 对于错误的下标，fetch返回NULL */
		.store_leakproof = false	/* ...但赋值抛出错误 */
	};

	PG_RETURN_POINTER(&fc_sbsroutines);
}
